package com.ruoyi.face.service;

import cn.hutool.crypto.SecureUtil;
import com.ruoyi.common.json.JSON;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.face.domain.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

@Service
public class FaceDeviceImpl implements FaceDevice {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private String host = "http://192.168.5.3";
    private String pathChallenge = "/api/auth/login/challenge";
    private String pathLogin = "/api/auth/login";
    private String pathMember = "/api/persons";
    private RestTemplate restTemplate = new RestTemplate();
    private volatile String sessionId;

    @Override
    public void doLogin() {
        try {
            ChallengeRsp rsp = challenge("admin");
            User user = new User();
            user.setUserName("admin");
            user.setPassword("admin123");
            user.setSessionId(rsp.getSessionId());
            login(user, rsp);
        } catch (Exception e) {
            sessionId = "";
            logger.error("device login error: {}", e.getMessage());
        }
    }

    @Override
    public void updateDeviceHost(String host) {
        this.host = host;
        logger.info("device api address: {}", host);
    }

    public ChallengeRsp challenge(String username) {
        return restTemplate.getForObject(host + pathChallenge + "?username={q}", ChallengeRsp.class, username);
    }

    public void login(User user, ChallengeRsp challengeRsp) {
        user.setPassword(SecureUtil.sha256(user.getPassword() + challengeRsp.getSalt() + challengeRsp.getChallenge()));
        ResponseEntity<User> entity = exchange(host + pathLogin, HttpMethod.POST, User.class, user);
        logger.info("device login : {}", entity);
        User result = entity.getBody();
        if (result.getStatus() != null && result.getStatus() == 200) {
            user.setSessionId(result.getSessionId());
            user.setStatus(result.getStatus());
            sessionId = user.getSessionId();
        } else {
            logger.warn("device login failed: {}", result.getStatus());
            sessionId = "";
        }
    }

    @Override
    public void addMember(Member member) {
        ResponseEntity<String> entity = exchange(host + pathMember + "/item", HttpMethod.POST, String.class, member);
        logger.info("add member: {}", entity);
    }

    @Override
    public Member getMemberById(Long id) {
        ResponseEntity<Member> entity = exchange(host + pathMember + "/item/" + id, HttpMethod.GET, Member.class, null);
        logger.debug("get member: {}", entity);
        return entity.getBody();
    }

    @Override
    public void updateMemberById(Member member) {
        ResponseEntity<Member> entity = exchange(host + pathMember + "/item/" + member.getId(), HttpMethod.PUT, Member.class, member);
        logger.info("add member: {}", entity);
    }

    @Override
    public void deleteMemberById(Long id) {
        ResponseEntity<String> entity = exchange(host + pathMember + "/item/" + id, HttpMethod.DELETE, String.class, null);
        logger.info("del member: {}", entity);
    }

    @Override
    public QueryResult<Member> queryMember(QueryCondition queryCondition) {
        ParameterizedTypeReference<QueryResult<Member>> rspType = new ParameterizedTypeReference<QueryResult<Member>>() {
        };
        ResponseEntity<QueryResult<Member>> entity = exchange(host + pathMember + "/query", HttpMethod.POST, rspType, queryCondition);
        logger.debug("query member: {}", entity);

        return entity.getBody();
    }

    @Override
    public boolean deleteMemberByIds(List<Long> ids) {
        BatchDel batchDel = new BatchDel();
        batchDel.setItems(ids);
        ResponseEntity<String> entity = exchange(host + pathMember + "/item", HttpMethod.DELETE, String.class, batchDel);
        logger.info("del members: {}", entity);
        return true;
    }

    @Override
    public void setSubscribePush(SubScribe subscribe) {
        ResponseEntity<String> entity = exchange(host + "/api/subscribe/push", HttpMethod.PUT, String.class, subscribe);
        logger.info("set subscribe: {}", entity);
    }

    @Override
    public boolean isLogin() {
        return StringUtils.isNotEmpty(sessionId);
    }

    @Override
    public void status() {
        ResponseEntity<String> entity = exchange(host + "/api/devices/status", HttpMethod.GET, String.class, null);
        logger.debug("get device status: {}", entity);
    }

    @Override
    public void setVoicePool(Voices voices) {
        ResponseEntity<String> entity = exchange(host + "/api/voice/pool/item", HttpMethod.POST, String.class, voices);
        logger.info("set voice pool: {}", entity);
    }

    @Override
    public void setCustomizeVoice(CustomizeVoices customizeVoice) {
        ResponseEntity<String> entity = exchange(host + "/api/voice/customize/item", HttpMethod.POST, String.class, customizeVoice);
        logger.info("set voice customize: {}", entity);
    }

    private <T> ResponseEntity<T> exchange(String url, HttpMethod httpMethod, Class<T> responseType, Object data) {
        HttpEntity<String> request = makeHttpEntity(data);
        return restTemplate.exchange(url, httpMethod, request, responseType);
    }

    private <T> ResponseEntity<T> exchange(String url, HttpMethod httpMethod, ParameterizedTypeReference<T> responseType, Object data) {
        HttpEntity<String> request = makeHttpEntity(data);
        return restTemplate.exchange(url, httpMethod, request, responseType);
    }

    private HttpEntity<String> makeHttpEntity(Object data) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        List<String> cookies = new ArrayList<>();
        cookies.add("sessionID=" + sessionId);
        headers.put(HttpHeaders.COOKIE, cookies);
        HttpEntity<String> request;
        try {
            if (data != null) {
                request = new HttpEntity<>(JSON.marshal(data), headers);
            } else {
                request = new HttpEntity<>(headers);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return request;
    }
}
